/**
 * @brief 结构化绑定
 * @copyright AI Generate
 */
#pragma once
#include <QApplication>
#include <QCheckBox>
#include <QLineEdit>
#include <QMainWindow>
#include <QPushButton>
#include <QSpinBox>
#include <QVBoxLayout>
#include <cstddef>
// #include <iostream>
// #include <type_traits>

// BUG 第一次数据无法显示到界面上

// 确保结构体数据是连续排列
#pragma pack(push, 1)
struct NetworkConfig {
    char    ip[16];
    quint16 port;
    bool    dhcp;
};

struct AppConfig {
    NetworkConfig network;
    struct {
        bool   debugMode;
        quint8 logLevel;
    } settings;
    double timeout;
};
#pragma pack(pop)

// 基础模板声明（处理基本类型）
// template <typename T, typename Enable = void>
// struct StructBinder {
//     template <typename F>
//     static void apply(T &data, F &&f) {  // 基本类型不需要展开
//     }
// };

/**
 * @brief apply函数就是手动将结构体展开，依次对数据成员进行绑定
 */

// POD结构体特化（处理NetworkConfig）
// template <>
// struct StructBinder<NetworkConfig> {
//     template <typename F>
//     static void apply(NetworkConfig &data, F &&f) {
//         f(data.ip, "ip");
//         f(data.port, "port");
//         f(data.dhcp, "dhcp");
//     }
// };

// POD结构体特化（处理AppConfig）
// template <>
// struct StructBinder<AppConfig> {
//     template <typename F>
//     static void apply(AppConfig &data, F &&f) {
//         f(data.network, "network");
//         f(data.settings.debugMode, "debugMode");
//         f(data.settings.logLevel, "logLevel");
//         f(data.timeout, "timeout");
//     }
// };

// 数组类型特化（处理char[16]等数组）
// template <typename T, size_t N>
// struct StructBinder<T[N]> {
//     template <typename F>
//     static void apply(T (&data)[N], F &&f) {  // 数组类型按单个元素处理
//         f(data, "array");
//     }
// };

class StructMapper : public QObject {
    Q_OBJECT
public:
    /**
     * @brief 显示构造函数
     * @param data 原始数据首地址，内部不负责数据的内存管理
     */
    explicit StructMapper(char *data) : m_data(data) {}

    /**
     * @brief 模板化构造函数
     * @param data
     */
    template <typename T>
    StructMapper(T &data) : StructMapper(reinterpret_cast<char *>(&data)) {
        // 该段代码毫无作用
        // initBindings<T>();
    }

    /**
     * @brief bind 将组件与数据的内存偏移进行绑定
     * @param widget 组件指针
     * @param fieldPath 标记
     */
    void bind(QWidget *widget, const std::string &fieldPath) {
        size_t offset = calculateOffset(fieldPath);
        if (offset != size_t(-1)) {
            m_bindings[widget] = offset;
            connectWidget(widget);
        }
    }

    /**
     * @brief 将数据加载到界面
     */
    void loadToWidgets() {
        for (auto &pair : m_bindings) {
            updateWidget(pair.first, pair.second);
        }
    }

public slots:
    void saveFromWidgets() {
        for (auto &pair : m_bindings) {
            updateData(pair.first, pair.second);
        }
    }

private:
    /**
     * @brief initBindings
     */
    // template <typename T>
    // void initBindings() {
    //     StructBinder<T>::apply(
    //         // 将数据转化为原始类型，调用对应的特化apply函数
    //         *reinterpret_cast<T *>(m_data),
    //         // remove_all_extents用于移除数据的维度信息，获取原始类型，即将cha*变为char
    //         // typename：用于告诉编译器 std::remove_all_extents<T>::type
    //         // 是一个类型而非成员变量
    //         [this](typename std::remove_all_extents<T>::type &member, const std::string &name) {
    //             // decay用于类型衰减，去掉类型的附加信息，方便计算偏移
    //             using MemberType = typename std::decay<decltype(member)>::type;
    //             // 计算结构体成员与结构体首地址的偏移
    //             const size_t offset = reinterpret_cast<size_t>(&member) -
    //                                   reinterpret_cast<size_t>(m_data);
    //             registerBinding<MemberType>(offset, name);
    //         });
    // }

    // template <typename MemberType>
    // void registerBinding(size_t offset, const std::string &name) {
    //     if (std::is_class<MemberType>::value) {
    //         // 仅对类类型进行递归绑定
    //         StructMapper *nestedMapper = new StructMapper(m_data + offset);
    //         nestedMapper->template initBindings<MemberType>();
    //     }
    // }

    /**
     * @brief 计算成员变量在结构体中的偏移
     * @param path 成员标记
     * @return
     */
    size_t calculateOffset(const std::string &path) {
        if (path == "network.ip") return offsetof(AppConfig, network) + offsetof(NetworkConfig, ip);
        if (path == "network.port") return offsetof(AppConfig, network) + offsetof(NetworkConfig, port);
        if (path == "network.dhcp") return offsetof(AppConfig, network) + offsetof(NetworkConfig, dhcp);
        if (path == "settings.debugMode") return offsetof(AppConfig, settings) + 0;
        if (path == "settings.logLevel") return offsetof(AppConfig, settings) + 1;
        if (path == "timeout") return offsetof(AppConfig, timeout);
        return -1;
    }

    /**
     * @brief connectWidget 连接信号与槽，当界面数据修改时，修改对应内存数据
     * @param widget
     */
    void connectWidget(QWidget *widget) {
        // 根据组件类型，连接不同信号
        if (auto spin = qobject_cast<QSpinBox *>(widget)) {
            connect(spin,
                    // 如果应该是想指定同名不同参数类型的信号函数，qt有现成的机制
                    // WARNING 这里信号函数只有一个，使用强制转换莫名其妙
                    // static_cast<void
                    // (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
                    &QSpinBox::valueChanged, [this](int) { saveFromWidgets(); });  // 用lambda表达式包裹以匹配信号类型
        } else if (auto spin = qobject_cast<QDoubleSpinBox *>(widget)) {
            connect(spin,
                    // static_cast<void (QDoubleSpinBox::*)(double)>(
                    //     &QDoubleSpinBox::valueChanged),
                    &QDoubleSpinBox::valueChanged, [this](double) { saveFromWidgets(); });
        }

        else if (auto edit = qobject_cast<QLineEdit *>(widget)) {
            connect(edit, &QLineEdit::editingFinished, this, &StructMapper::saveFromWidgets);
        }

        else if (auto check = qobject_cast<QCheckBox *>(widget)) {
            connect(check,
                    // static_cast<void (QCheckBox::*)(int)>(&QCheckBox::stateChanged),
                    &QCheckBox::stateChanged, [this](int) { saveFromWidgets(); });
        } else {
            // qWarning() << "Unsupported widget type:" <<
            // widget->metaObject()->className();
        }
    }

    /**
     * @brief updateWidget 使用内存中数据更新界面数据
     * @param widget
     * @param offset
     */
    void updateWidget(QWidget *widget, size_t offset) {
        char *data = m_data + offset;
        if (auto spin = qobject_cast<QSpinBox *>(widget)) {
            // 因为结构体中包含两个spinBox对应的数据，所以需要分开处理
            if (offset == offsetof(AppConfig, network) + offsetof(NetworkConfig, port)) {
                spin->setValue(static_cast<quint16>(*data));
            } else {
                spin->setValue(static_cast<quint16>(*data));
            }
        }

        else if (auto edit = qobject_cast<QLineEdit *>(widget)) {
            edit->setText(QString::fromLocal8Bit(reinterpret_cast<char *>(data)));
        } else if (auto check = qobject_cast<QCheckBox *>(widget)) {
            check->setChecked(*reinterpret_cast<bool *>(data));
        } else if (auto spin = qobject_cast<QDoubleSpinBox *>(widget)) {
            spin->setValue(*reinterpret_cast<double *>(data));
        } else {
            // error
        }
    }

    void updateData(QWidget *widget, size_t offset) {
        char *data = m_data + offset;
        if (auto spin = qobject_cast<QSpinBox *>(widget)) {
            // 因为结构体中包含两个spinBox对应的数据，所以需要分开处理
            if (offset == offsetof(AppConfig, network) + offsetof(NetworkConfig, port)) {
                *reinterpret_cast<quint16 *>(data) = spin->value();
            } else {
                *reinterpret_cast<int *>(data) = spin->value();
            }
        } else if (auto spin = qobject_cast<QDoubleSpinBox *>(widget)) {
            *reinterpret_cast<double *>(data) = spin->value();
        }

        else if (auto edit = qobject_cast<QLineEdit *>(widget)) {
            strncpy(reinterpret_cast<char *>(data), edit->text().toLocal8Bit().constData(), 16);
        }

        else if (auto check = qobject_cast<QCheckBox *>(widget)) {
            *reinterpret_cast<bool *>(data) = check->isChecked();
        }
    }

private:
    char                       *m_data;      // 数据存放首地址，数据紧密排列在连续空间段内
    std::map<QWidget *, size_t> m_bindings;  // 控件与数据的映射关系
};
